﻿using AlgoritmoConstrutivo.entidades;
using Entidades;
using Entidades.entidades;
using RandomKeyGenerator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Entidades
{
    public class BRKGA
    {
        private Cromossomo [] populacao;
        private List<GrafoResumido> grafos;
        private Cromossomo[] novaPopulacao;
        private FabricaCromossomos fabricaRandomKeys; //fabrica de numeros aleatorios
        private int numGenes;

     
        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="cromossomos">Lista de vetores de N posições com os numeros aleatorios. Cada vetor representa uma solução para o CSM</param>
        /// <param name="grafos">Resumo com as informações relevantes de cada vetor</param>
        /// <param name="numGenes">Numero de genes. Corresponde ao número de vértices de um grafo no CSM</param>
        public BRKGA(Cromossomo[] cromossomos, List<GrafoResumido> grafos, int numGenes)
        {
            this.populacao = cromossomos;
            //os cromossomos chegam neste ponto sem estarem ordenados por suas chaves aleatórias.

            this.grafos = grafos;

            this.numGenes = numGenes;

            this.fabricaRandomKeys = new FabricaCromossomos(this.numGenes);

            this.novaPopulacao = new Cromossomo[this.populacao.Count()];
        }
      
        public Cromossomo[] GenerateNewPopulation()
        {

           //this.CopyEliteToNextGeneration();

           //this.GenerateMutants();
           //this.GenerateRestOfPopulation();





            return this.novaPopulacao;
          
        }

        private void CopyEliteToNextGeneration(Configuration2 cfg)
        {

         //   for (int i = 0; i < Configuration.PopulationElite; i++)

            for (int i = 0; i < cfg.PopulationElite; i++)
            {
                this.novaPopulacao[i] = new Cromossomo();
                this.novaPopulacao[i].Chaves = this.populacao[grafos[i].Individuo].Chaves;
                this.novaPopulacao[i].Value = this.populacao[grafos[i].Individuo].Value;
                this.novaPopulacao[i].FitnessCalculado = this.populacao[grafos[i].Individuo].FitnessCalculado;
            }
        }

        private void GenerateMutants(Configuration2 cfg)
        {
            int numCromossomos = cfg.Population;
            numCromossomos--;

            int count = (int)(this.populacao.Count() - cfg.PopulationMutant);
            for (int i = numCromossomos; i >= (this.populacao.Count() - cfg.PopulationMutant); i--)
            {

            //Parallel.For(0, count, i =>
            //{
                this.novaPopulacao[i] = new Cromossomo();
                this.novaPopulacao[i].Chaves = this.fabricaRandomKeys.GenerateRandomKeys();
                //this.novaPopulacao[i].Value = Cluster.GerarNumeroClusters(false, this.numGenes);
            }
            //});


        }

        private void GenerateRestOfPopulation(Configuration2 cfg)
        {
            int paiElite = 0;
            int PaiNaoElite = 0;

            int elite = (int) cfg.PopulationElite;
            elite--;

            int eliteMaisUm = elite;
            eliteMaisUm++;

            int populacao = cfg.Population;
            populacao--;


            int numPopulacaoRestante = (int)cfg.Population - (int)(cfg.PopulationElite + cfg.PopulationMutant);            

            for (int i = 0; i < numPopulacaoRestante; i++)
            {
                //selecionou os progenitores
                paiElite = RandomNumberGenerator.GenerateRandomNumberFromInterval(0, elite);
                PaiNaoElite = RandomNumberGenerator.GenerateRandomNumberFromInterval(eliteMaisUm, populacao);

                int a = (int)cfg.PopulationElite + i;
                this.novaPopulacao[a] = this.CrossOver(paiElite, PaiNaoElite,null);
              

               
            }
        }

        private Cromossomo CrossOver(int paiElite, int PaiNaoElite, Configuration2 cfg)
        {
            Cromossomo aleloElite = new Cromossomo();
            aleloElite.Chaves = new Gene[this.numGenes];

            Cromossomo aleloNelite = new Cromossomo();
            aleloNelite.Chaves = new Gene[this.numGenes];

            //simplesmente faço uma copia do cromossomo elite escolhido
            for (int i = 0 ; i < this.numGenes ; i++)
            {
                Gene rkNovo = new Gene();

               // rkNovo.Indice = i; 
                rkNovo.Indice = this.populacao[grafos[paiElite].Individuo].Chaves[i].Indice;

                rkNovo.KeyOrdenacao = this.populacao[grafos[paiElite].Individuo].Chaves[i].KeyOrdenacao;
                rkNovo.KeyCriterio = this.populacao[grafos[paiElite].Individuo].Chaves[i].KeyCriterio;
                aleloElite.Value = this.populacao[grafos[paiElite].Individuo].Value;

                aleloElite.Chaves[i] = rkNovo;
            }

            //simplesmente faço uma copia do cromossomo nao elite escolhido
            for (int i = 0; i < this.numGenes; i++)
            {
                Gene rkNovo = new Gene();

               // rkNovo.Indice = i;
                rkNovo.Indice = this.populacao[grafos[PaiNaoElite].Individuo].Chaves[i].Indice;

                rkNovo.KeyOrdenacao = this.populacao[grafos[PaiNaoElite].Individuo].Chaves[i].KeyOrdenacao;
                rkNovo.KeyCriterio = this.populacao[grafos[PaiNaoElite].Individuo].Chaves[i].KeyCriterio;
                aleloNelite.Value = this.populacao[grafos[PaiNaoElite].Individuo].Value;

                aleloNelite.Chaves[i] = rkNovo;
           
            }
      
            Cromossomo prole = new Cromossomo();
            prole.Chaves = new Gene[this.numGenes];


            //código específico para o "valor" que não é uma chave aleatória
            double rowDiceForValue = this.fabricaRandomKeys.RowBiasedDice();

            if (rowDiceForValue > cfg.PopulationInheritanceProbability)
            
                prole.Value = aleloNelite.Value;
            else
                prole.Value = aleloElite.Value;


            //código específico para as chaves aleatórias em si.
            for (int i = 0; i <this.numGenes; i++)
            {
               double rowDice =  this.fabricaRandomKeys.RowBiasedDice();

                //o valor de probabilidade de herança vai variar de 50 a 80%, logo quem for maior do que ele, tem a menor probabilidade.
               if (rowDice > cfg.PopulationInheritanceProbability)
               {
                   prole.Chaves[i] = aleloNelite.Chaves[i];
                  
               }

               else
               {
                   prole.Chaves[i] = aleloElite.Chaves[i];
                 

               }
                //eu nao preciso herdar o indice pois neste ponto a primeira posição do vetor, é de fato do modulo lido na instância. Até este momento nenhuma ordenação foi feita.
               prole.Chaves[i].Indice = i;               
             
            }

            return prole;
        }

      

     
    }
}
